Convert .blg to CSV
# Skip conversion if CSV already exists
if (!file.exists("perfmon.csv")) {
blg_files <- list.files(pattern = "\\.blg$", full.names = TRUE)
blg_file <- blg_files[which.max(file.info(blg_files)$mtime)]
message("Starting relog conversion for: ", blg_file)
system2(
"relog",
args = c(shQuote(blg_file), "-f", "CSV", "-o", "perfmon.csv"),
stdout = "", stderr = ""
)
if (!file.exists("perfmon.csv")) stop("perfmon.csv not created. Check relog call.")
} else {
message("perfmon.csv already exists; skipping conversion.")
}
Read & Parse Model Run Log
model_files <- list.files(pattern = "^tm2py_run.*\\.log$", full.names = TRUE)
message("Found log files: ", paste(basename(model_files), collapse=", "))
if (length(model_files) == 0) {
stop("No tm2py_run log files found in working directory: ", getwd())
}
model_log <- model_files[which.max(file.info(model_files)$mtime)]
message("Using log file: ", basename(model_log))
lines <- readLines(model_log)
# Define pattern: captures Date, Time, Level, and Message
datetime_str <- gsub("[()]", "", substr(lines, 1, 22))
Timestamp <- dmy_hms(datetime_str)
# Extract Level and Message by splitting at first colon after timestamp
# Remove the timestamp and any leading spaces
rest <- substr(lines, 23, nchar(lines))
# Level is before first ':'
Level <- str_trim(sub(":.*$", "", rest))
# Message is after the first ':'
Message <- str_trim(sub("^[^:]+:\\s*", "", rest))
# Build events tibble
model_events <- tibble(
Timestamp = Timestamp,
Level = Level,
Message = Message
) %>%
filter(Level %in% c("INFO", "STATUS"))
Align Data
# Filter PerfMon to last event timestamp
cutoff_ts <- max(model_events$Timestamp)
perf <- perf %>% filter(Timestamp <= cutoff_ts)
message("PerfMon data filtered through ", cutoff_ts)
Faceted Plot of Metrics with Event Labels
Individual Interactive Charts with Significant Event
Annotations